package Java工具收集;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.maven.shared.invoker.DefaultInvocationRequest;
import org.apache.maven.shared.invoker.DefaultInvoker;
import org.apache.maven.shared.invoker.InvocationOutputHandler;
import org.apache.maven.shared.invoker.InvocationRequest;
import org.apache.maven.shared.invoker.Invoker;

import com.alibaba.fastjson.JSONObject;


/**
 * 
 * Verbose not supported since maven-dependency-plugin 3.0
   
   在要执行的pom文件中,使用低一点的版本看看 ,发现可以打印出冲突
   
   <plugin>
		<groupId>org.apache.maven.plugins</groupId>
		<artifactId>maven-dependency-plugin</artifactId>
		<version>2.10</version>
	</plugin>
 * 
 * 
 * 注 : 数仓规划项目 , 我把所有有冲突依赖都排除后 , 还是无法启动 . 
 * 原因是 , 有些jar包的高版本连架构都改变了 , 有些方法根本就不存在 , 所以排除依赖不是彻底的解决方案.
 * 
 * 所以还是得用土办法解决
 *
 */
public class PomConflictUtils {
	
	public static void main(String[] args)  {
		
//		String pomPath = "D:/eclipse-workspace/MyJava/pom.xml";
		
		//数仓规划的pom
		String pomPath = "D:\\eclipse-workspace\\km\\km-service\\pom.xml";
//		String pomPath = "D:/eclipse-workspace/ewell-dwplan/pom.xml";
		
		String mavenHome = "E:/My_WorkSoft/apache-maven-3.6.3";
		
		String txt = createDependenciesTxt(pomPath, mavenHome);
		
		//拷贝到剪切板
		ClipboardUtils.setClipboardString(txt);
		
		System.out.println(txt);
		
	}
	
	/**
	 * @function 重新生成依赖文本
	 * @author 肖荣辉
	 * @date 2021年4月7日
	*/
	public static String createDependenciesTxt(String pomPath, String mavenHome) {
		
		List<List<String>> textLineList = getDependencyTextLineList(pomPath, mavenHome);
		
		List<Artifact> artifactList = new ArrayList<Artifact>();
		
		for(List<String> list : textLineList) {
			
			//第一个是父亲
			String parent = list.get(0);
			
			Artifact parentArtifact = analysisArtifactInfo(parent);
			artifactList.add(parentArtifact);
			
			if(list.size() == 1) continue;
			
			for(String subArtifactInfo : list) {
				parentArtifact.getChildArtifactList().add(analysisArtifactInfo(subArtifactInfo));
			}
			
		}
		
		//相同版本的有冲突的放到一起
		Map<String , List<Artifact>> conflictArtifactMap = new HashMap<String, List<Artifact>>();
		
		//子节点和父节点对照map
		Map<String , String> childIdParentIdMap = new HashMap<String, String>();
		
		//挑出已经标记为已经冲突的jar包
		for(Artifact item : artifactList) {
			
			if(item.isConflict()) {
				String key = item.getGroupId() + "_" + item.getArtifactId();
				List<Artifact> conflictArtifactList = Optional.ofNullable(conflictArtifactMap.get(key)).orElse(new ArrayList<Artifact>());
				conflictArtifactList.add(item);
				conflictArtifactMap.put(key, conflictArtifactList);
				childIdParentIdMap.put(item.getId(), item.getId());
			}
			
			for(Artifact child : item.getChildArtifactList()) {
				
				String childKey = child.getGroupId() + "_" + child.getArtifactId();
				
				List<Artifact> childConflictArtifactList = Optional.ofNullable(conflictArtifactMap.get(childKey)).orElse(new ArrayList<Artifact>());
				
				if(child.isConflict()) {
					childConflictArtifactList.add(child);
					conflictArtifactMap.put(childKey, childConflictArtifactList);
					childIdParentIdMap.put(child.getId(), item.getId());
				}
				
			}
		}
		
		//挑出那些未标志为冲突的相同jar包, 也放到冲突里面
		for(Artifact item : artifactList) {
			
			String key = item.getGroupId() + "_" + item.getArtifactId();
			
			//不冲突,且包含的情况下
			if(!item.isConflict() && conflictArtifactMap.containsKey(key)) {
				List<Artifact> conflictArtifactList = Optional.ofNullable(conflictArtifactMap.get(key)).orElse(new ArrayList<Artifact>());
				conflictArtifactList.add(item);
				conflictArtifactMap.put(key, conflictArtifactList);
				childIdParentIdMap.put(item.getId(), item.getId());
			}
			
			for(Artifact child : item.getChildArtifactList()) {
				
				String childKey = child.getGroupId() + "_" + child.getArtifactId();
				
				List<Artifact> childConflictArtifactList = Optional.ofNullable(conflictArtifactMap.get(childKey)).orElse(new ArrayList<Artifact>());
				
				if(!child.isConflict()  && conflictArtifactMap.containsKey(childKey)) {
					childConflictArtifactList.add(child);
					conflictArtifactMap.put(childKey, childConflictArtifactList);
					childIdParentIdMap.put(child.getId(), item.getId());
				}
				
			}
		}
		
		//分析出最高版本是谁 , 然后剔除最高版本 , 其它的统统剔除
		List<ArtifactVersionBean> artifactVersionBeanList =  analysisArtifactVersionBeanList(conflictArtifactMap, childIdParentIdMap);
		
		//如果根节点自己要移除 , 则把该根节点注释掉 , 但是又怕他自己本身有要剔除的jar包,这样就导致有些jar包就不见了
		//如果根节点自己要移除自己的, 看看是否要剔除其它的jar包  , 如果要剔除, 注释掉自己,对外面毫无影响 . 如果不要剔除, 就有可能包含
		//的是最高版本的依赖, 刚好这个根节点要被移除. 如果根节点要被移除, 最高版本的依赖,重新生成.
		//我得先得到一个最高版本依赖对应的根节点ID的集合 , 当要移除根节点的时候, 判断当前根节点是否在前一个集合中存在 , 如果存在, 剔除根节点, 并生成最高版本的依赖
		return createDependenciesTxt(artifactList , artifactVersionBeanList);
		
	}
	
	
	/**
	 * @function 重新生成依赖文本
	 * @author 肖荣辉
	 * @date 2021年4月7日
	*/
	@SuppressWarnings("unchecked")
	public static String  createDependenciesTxt(List<Artifact> artifactList , List<ArtifactVersionBean> artifactVersionBeanList) {
		
		if(CollectionUtils.isEmpty(artifactVersionBeanList))
			return "";
		
		Map<String , String> childIdParentIdMap = artifactVersionBeanList.get(0).getChildIdParentIdMap();
		
		//我需要一个map,当前根节点的id对应哪些要剔除的版本号
		Map<String , List<Artifact>> idExcludeConflictArtifactListMap = getIdExcludeConflictArtifactListMap(artifactVersionBeanList);
		
		//最高版本的jar包集合
		 List<Artifact> highestVersionArtifactList = new ArrayList<Artifact>(CollectionUtils.collect(artifactVersionBeanList,   o -> ((ArtifactVersionBean)o).getHighestVersionArtifact()));
		
		 //根节点和对应的最高版本集合对照map
		 Map<String , List<Artifact>>  rootIdHighestVersionArtifactListMap = new HashMap<String, List<Artifact>>();
		 
		 for(Artifact item : highestVersionArtifactList) {
			 
			 String parentId = childIdParentIdMap.get(item.getId());
			 
			 List<Artifact> listTemp = Optional.ofNullable(rootIdHighestVersionArtifactListMap.get(parentId)).orElse(new ArrayList<Artifact>());
			 
			 listTemp.add(item);
			 
			 rootIdHighestVersionArtifactListMap.put(parentId, listTemp);
		 }
		 
		 
		String txt = "";
		
		for(Artifact item :  artifactList) {
			
			List<Artifact> excludeConflictArtifactList =  idExcludeConflictArtifactListMap.get(item.getId());
			
			//生成排除依赖的文本
			String excludeTxt ="";
			
			//是否是自己剔除自己
			boolean isRemoveSelf = false;
			
			if(CollectionUtils.isNotEmpty(excludeConflictArtifactList)) {
				
				//提取出所有的ID, 得看看是否存在自己剔除自己的情况
				 List<String> conflictIdList = new ArrayList<String>(CollectionUtils.collect(excludeConflictArtifactList,   o -> ((Artifact)o).getId()));
				 
				 //若要移除自己, 则设置标志
				 if(conflictIdList.contains(item.getId())) {
					 isRemoveSelf = true;
				 }
				 //若不要移除自己 , 则剔除冲突jar包
				 else {
					 
					 excludeTxt += "\n\t<exclusions>";
						
						for(Artifact excludeArtifact : excludeConflictArtifactList) {
								
							excludeTxt += "\n\t\t<exclusion>" + 
														"\n\t\t\t<groupId>"+excludeArtifact.getGroupId()+"</groupId>" + 
														"\n\t\t\t<artifactId>"+excludeArtifact.getArtifactId()+"</artifactId>" + 
													  "\n\t\t</exclusion> ";
						}
						
						excludeTxt += "\n\t</exclusions>";
				 }
			}
			
			
			String dependencyTxt = "\n<dependency>" + 
														"\n\t<groupId>"+item.getGroupId()+"</groupId>" + 
														"\n\t<artifactId>"+item.getArtifactId()+"</artifactId>" + 
														"\n\t<version>"+item.getVersion()+"</version>" + excludeTxt + 
													"\n</dependency>";
			
			if(isRemoveSelf) 
				dependencyTxt = "\n<!-- 该依赖是低版本的jar包 , 在别的依赖项中已经被依赖 , 在这里注释掉 -->\n<!--" + dependencyTxt + "\n-->";
			
			
			//若是自己移除自己 , 同时自己又包含最高的版本 , 则把最高版本的依赖到根下面
			if(isRemoveSelf && rootIdHighestVersionArtifactListMap.containsKey(item.getId())) {
				
				List<Artifact> listTemp = rootIdHighestVersionArtifactListMap.get(item.getId());
				
				String txtTemp = "";
				
				for(Artifact highArtifact : listTemp) {
					
					txtTemp += "\n\n<dependency>" + 
											"\n\t<groupId>"+highArtifact.getGroupId()+"</groupId>" + 
											"\n\t<artifactId>"+highArtifact.getArtifactId()+"</artifactId>" + 
											"\n\t<version>"+highArtifact.getVersion()+"</version>"+
										"\n</dependency>";
					
				}
				
				txt += txtTemp;
				
				continue;
			}
			
			txt += "\n" + dependencyTxt;
		}
		
		return txt;
	}
	
	
	
	/**
	 * @function 获取id和要剔除的jar集合对照map
	 * @author 肖荣辉
	 * @date 2021年4月7日
	*/
	public static Map<String , List<Artifact>> getIdExcludeConflictArtifactListMap(List<ArtifactVersionBean> artifactVersionBeanList){
		
		Map<String , List<Artifact>> idExcludeConflictArtifactListMap = new  HashMap<String , List<Artifact>>();
		
		for(ArtifactVersionBean item : artifactVersionBeanList) {
			
			List<Artifact> excludeConflictArtifactList = item.getExcludeConflictArtifactList();
			
			Map<String , String> childIdParentIdMap = item.getChildIdParentIdMap();
			
			for(Artifact artifact : excludeConflictArtifactList) {
				
				String parentId = childIdParentIdMap.get(artifact.getId());
				
				List<Artifact> artifactList = Optional.ofNullable(idExcludeConflictArtifactListMap.get(parentId)).orElse(new ArrayList<Artifact>());
				
				artifactList.add(artifact);
				
				idExcludeConflictArtifactListMap.put(parentId, artifactList);
				
			}
			
		}
		
		return idExcludeConflictArtifactListMap;
		
	}
	
	
	
	/**
	 * @function 从冲突的jar包集合中分析出最高版本号是谁 , 以及相关信息封装到一个对象中 , 得到一个集合
	 * @param 
	 * @author 肖荣辉
	 * @date 2021年4月7日
	*/
	public static List<ArtifactVersionBean>  analysisArtifactVersionBeanList(Map<String , List<Artifact>> conflictArtifactMap , Map<String , String> childIdParentIdMap){
		
		List<ArtifactVersionBean>  artifactVersionBeanList = new ArrayList<ArtifactVersionBean>();
		
		for(String key : conflictArtifactMap.keySet()) {
			
			List<Artifact> conflictArtifactList = conflictArtifactMap.get(key);
			
			artifactVersionBeanList.add(analysisConflictArtifactList(conflictArtifactList , childIdParentIdMap));
		}
		
		return artifactVersionBeanList;
	}
	
	
	/**
	 * @function 从冲突的jar包集合中分析出最高版本号是谁 , 以及相关信息封装到一个对象中
	 * @author 肖荣辉
	 * @date 2021年4月7日
	*/
	@SuppressWarnings("unchecked")
	public static ArtifactVersionBean analysisConflictArtifactList(List<Artifact> conflictArtifactList , Map<String , String> childIdParentIdMap) {
		
		 List<String> conflictVersionList = new ArrayList<String>(CollectionUtils.collect(conflictArtifactList,   o -> ((Artifact)o).getVersion()));
		
		 int highestVersionIndex = getHighestVersionIndex(conflictVersionList);
		 
		 Artifact  highestVersionArtifact = conflictArtifactList.get(highestVersionIndex);
		 
		 //剔除最高的那个版本号, 剩余的就是要剔除的
		 List<Artifact> excludeConflictArtifactList =  new ArrayList<Artifact>(conflictArtifactList);
		 excludeConflictArtifactList.remove(highestVersionIndex);
		 
		 String conflictGroupId = highestVersionArtifact.getGroupId();
		 
		 String conflictArtifactId = highestVersionArtifact.getArtifactId();
		
		return new  ArtifactVersionBean(highestVersionArtifact, conflictArtifactList, excludeConflictArtifactList, conflictVersionList, conflictGroupId, conflictArtifactId , childIdParentIdMap);
		
	}
	
	
	/**
	 * @function 获取最高版本号的索引
	 * @param versionString
	 * @author 肖荣辉
	 * @date 2021年4月7日
	*/
	public static  int getHighestVersionIndex(List<String> conflictVersionList) {
		
		System.out.println("从 : " +  conflictVersionList + "挑选出最高的版本号");
		
		//返回最高版本号的索引
		List<Double> doubleVersionList = new ArrayList<Double>();
		
		for(String item : conflictVersionList) {
			doubleVersionList.add(convert2double(getNumberVersion(item)));
		}
		
		double max = doubleVersionList.get(0);
		
		int  maxIndex = 0;
		
		for(int i = 0 ;  i < doubleVersionList.size() ; i ++) {
			
			double item = doubleVersionList.get(i);
			
			if(item > max) {
				maxIndex = i;
				max = item;
			}
			
		}
		
		System.out.println("版本号最大的是 : " +  conflictVersionList.get(maxIndex) );
		
		return maxIndex;
	}
	
	
	/**
	 * @function 若版本号为空则返回-1
	 * @param versionNumber
	 * @author 肖荣辉
	 * @date 2021年4月7日
	*/
	public static double convert2double(String versionNumber) {
		
		if(StringUtils.isBlank(versionNumber))
			return -1;
		
		//4.3.2 , 只保留一个小数点 , 若没有小数点则直接返回
		if(versionNumber.indexOf(".") == -1)
			return Double.parseDouble(versionNumber);
		
		String prev = versionNumber.substring(0 , versionNumber.indexOf("."));
		String next = versionNumber.substring(versionNumber.indexOf(".") + 1);
		
		next = next.replaceAll("\\.", "");
		
		return Double.parseDouble(prev + "." + next) ;
		
	}
	
	
	/**
	 * @function 获取数字部分的版本号
	 * @param versionString
	 * @author 肖荣辉
	 * @date 2021年4月7日
	*/
	public static  String getNumberVersion(String versionString) {
		
		Pattern pattern = Pattern.compile("(\\d+(\\.\\d+)*)[\\s\\S]*");
		
		Matcher match = pattern.matcher(versionString);
		
		while(match.find()) {
			return match.group(1);
		}
		
		return "";
	}
	
	
	/**
	 * @function 从io.protostuff:protostuff-api:jar:1.7.2:compile这样的信息中分析出对象
	 * @param artifactInfo jar包信息
	 * @author 肖荣辉
	 * @date 2021年4月6日
	*/
	public static Artifact analysisArtifactInfo(String artifactInfo) {
		
		//替换前面的缩进和-左右的空格
		String handleArtifactInfo = artifactInfo.replaceAll("(\\|*(\\s+\\|)*(\\s)*(\\\\)*((\\+|\\\\)-)+)", "").replaceAll("\\(", "").replaceAll("\\)", "").replaceAll("\\s*-\\s*", "-").trim();
		
		String conflictInfo = "";
		
		boolean isConflict = false;
		
		//有冲突或者重复
		if(handleArtifactInfo.indexOf("-") != -1) {
			
			String endText = handleArtifactInfo.substring(handleArtifactInfo.lastIndexOf("-") + 1);
			
			//若冲突信息中有冒号 , 证明jar包名称中间有- , 没有:的才是
			if(endText.indexOf(":") == -1) {
				handleArtifactInfo = handleArtifactInfo.substring(0 , handleArtifactInfo.lastIndexOf("-")).trim();
				conflictInfo = endText.trim();
				isConflict = conflictInfo.contains("conflict");
			}
			
		}
		
		//示例 : org.springframework:spring-test:jar:4.3.2.RELEASE:test
		
		String[] arr = handleArtifactInfo.split(":");
		String groupId = arr[0];
		String artifactId = arr[1];
//		String jar = arr[2];
		String version = arr[3];
		
		return new  Artifact(groupId.trim(), artifactId.trim(), version.trim(), conflictInfo.trim(), isConflict);
	}
	
	/**
	 * @function 获取依赖节点的文本表示集合
	 * @param pomPath pom文件所在位置
	 * @param mavenHome maven 安装根目录所在位置
	 * @author 肖荣辉
	 * @date 2021年4月6日
	*/
	public static List<List<String>>  getDependencyTextLineList(String pomPath , String mavenHome) {
		
		//先归纳到同一组, 已划分父子级关系
		List<List<String>> textLineList = new ArrayList<List<String>>();
		
		try {
			
			InvocationRequest request = new DefaultInvocationRequest();
			request.setPomFile( new File(pomPath));
			request.setGoals(Collections.singletonList( "dependency:tree -Dverbose"));
			 
			Invoker invoker = new DefaultInvoker();
			invoker.setMavenHome(new File(mavenHome));
			
			List<String> temp = new ArrayList<String>();
			
			//结束标志
			final Set<String> isEndSet = new HashSet<String>();
			
			//这里是异步的
			invoker.setOutputHandler(new InvocationOutputHandler() {
				
				public void consumeLine(String line) throws IOException {
					
					System.out.println(line);
					 
					line = line.replaceAll("\\s*\\[INFO\\]\\s*", "");
					
//					System.out.println(line);
					
					//最后一个标签的后几行
 					 if(isEndSet.size() > 0 && line.matches("(\\|*(\\s+\\|)*(\\s)*(\\\\)*((\\+|\\\\)-)+)[\\s\\S]*")) {
						temp.add(line);
					}
 					
 					 //若已经彻底结束
 					else if(isEndSet.size() > 0 && !line.matches("(\\|*(\\s+\\|)*(\\s)*(\\\\)*((\\+|\\\\)-)+)[\\s\\S]*") && temp.size() > 0) {
 						textLineList.add(new ArrayList<String>(temp));
 						temp.clear();
					}	 
 					 
					//当时最后一个时 , 后面的统统加到里面去
 					else if(line.startsWith("\\-")) {
						
						//若上一个temp不为空则加到数组里面
						if(temp.size() > 0) {
							textLineList.add(new ArrayList<String>(temp));
							temp.clear();
						}
						
						isEndSet.add("1");
						temp.add(line);
						
					}
 					else if(line.startsWith("+-")) {
						
						//若上一个temp不为空则加到数组里面
						if(temp.size() > 0) {
							textLineList.add(new ArrayList<String>(temp));
							temp.clear();
						}
						
						temp.add(line);
						
					}else if(line.matches("(\\|*(\\s+\\|)*(\\s)*(\\\\)*((\\+|\\\\)-)+)[\\s\\S]*")) {
						temp.add(line);
					}
				
				}
				
			});
			
			invoker.execute(request);
			
			//美化后的json
			String json = JSONObject.toJSONString(textLineList , true);
			
			System.out.println(json);
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return textLineList;
	}
	
	
}
